from .Sqlite3CellReader import Sqlite3CellReader
from .Sqlite3Common import *
from .Sqlite3FreeBlock import Sqlite3FreeBlockParser

'''
假设sqlite3数据库使用的页大小0x1000
页大小4K块物理连续
未判定 leaf page 是否归属于目标页
根据第一个cell的地址推断当前页实际大小
网址的数据不长，默认数据不溢出
cell:
cellsize rowid [headersize type1~typen] [datai~datan]
cellsize = headersize + sum(datai~datan)

按页读取数据内容后，再判断数据是否符合要求
'''


class Sqlite3PageSearch:
    def __init__(self):
        self.pos_list = []

    def search(self, data: bytes):
        try:
            self._search(data)

        finally:
            return self.pos_list

    def _search(self, data: bytes):
        pos = -1
        step = 1
        page_parser = Sqlite3PageParser()
        while True:
            pos = data.find(LEAF_PAGE_FLAG, pos+step)
            if pos == -1 or pos + DEFAULT_PAGE_SIZE > len(data):
                break
            if page_parser.parse_leafpage(data[pos:pos+DEFAULT_PAGE_SIZE]):
                self.pos_list.append(pos)
                step = DEFAULT_PAGE_SIZE
            else:
                step = 1


class Sqlite3PageParser:
    def __init__(self):
        self.cells = []
        self.cells_pos_addr = 8
        self.cells_pos_addr_end = 0
        self.first_cell_addr = None
        self.freeblocks = []
        self.pagedata = None
        self.page_size = DEFAULT_PAGE_SIZE

    def parse_leafpage(self, pagedata):
        if not pagedata or len(pagedata) != DEFAULT_PAGE_SIZE or pagedata[0] != LEAF_PAGE_FLAG:
            return False
        '''
        如pagehead： 
        0d 00 00 00 0d 0d 94 00  0f e6 0f d0 0f 93 0f 6a
        0f 48 0e ff 0e bb 0e 9b  0e 6a 0e 3c 0d fe 0d b1
        0d 94 00 00 00 00 00 00  00 00 00 00 00 00 00 00
        first  0d94
        则在pagehead中有cell_list：[0f e6 0f d0 0f 93 0f 6a 0f 48 0e ff 0e bb 0e 9b  0e 6a 0e 3c 0d fe 0d b1 0d 94 00 00]
        具有特征从pagehead[8]开始，列表最后为record区域首地址(page中从尾部开始向上写数据，最新写的地址最靠前，在列表最后)，且地址后0x0000
        celllist中的数量符合[3:5]的值
        依据存在性意义判定，所需叶子页头部无超过4K大小的可能：(0x1000-8)/2*16 > 0x7000, 仅读取0x1000大小来推断实际页大小
        '''
        cells_count = conver_bytes_to_int(pagedata[3:5])
        first_cell_addr = pagedata[5:7]
        if not cells_count > 0:
            return False
        end_addr_pos = self.cells_pos_addr + cells_count * 2
        if pagedata[end_addr_pos:end_addr_pos+2] != b'\x00\x00':
            return False

        end_addr_pos = self.cells_pos_addr
        while True:
            if end_addr_pos+4 < DEFAULT_PAGE_SIZE and pagedata[end_addr_pos:end_addr_pos+2] != b'\x00\x00':
                end_addr_pos += 2
                continue
            break
        if pagedata[end_addr_pos-2:end_addr_pos] == first_cell_addr:
            self.cells_pos_addr_end = end_addr_pos
            self.first_cell_addr = first_cell_addr
            self.pagedata = pagedata
            return True

        return False

    def reset_page_size(self):
        max_offset = conver_bytes_to_int(self.first_cell_addr)
        for e in PAGE_SIZE_LIST:
            if max_offset < e:
                self.page_size = e
        return self.page_size

    def reset_pagedata(self, pagedata):
        self.pagedata = pagedata

    def parse_cells(self):
        records = []
        if not self.cells_pos_addr_end:
            return records

        for ele in range(self.cells_pos_addr, self.cells_pos_addr_end, 2):
            try:
                addr = conver_bytes_to_int(self.pagedata[ele:ele+2])
                if addr > self.page_size - MIN_CELL_SIZE:
                    continue
                record = Sqlite3CellReader(page_size=self.page_size).parse(self.pagedata, addr)
                if record:
                    records.append(record)
            except Exception as err:
                print(err)
                break
        self.cells = records
        return records

    def parse_freeblocks(self):
        if not self.cells or not isinstance(self.cells, list) or not self.cells[0]:
            return None
        first_free_block_add = conver_bytes_to_int(self.pagedata[1:3])
        if first_free_block_add == 0:
            return None
        parser = Sqlite3FreeBlockParser(self.page_size)
        if not parser.set_current_page_feature(self.cells[0]):
            return None
        return parser.parse(self.pagedata, first_free_block_add)
